package com.investment.advisor.service.impl;

import com.investment.advisor.dto.FundDTO;
import com.investment.advisor.dto.PortfolioDTO;
import com.investment.advisor.dto.StrategyDTO;
import com.investment.advisor.entity.Fund;
import com.investment.advisor.entity.Portfolio;
import com.investment.advisor.entity.PortfolioFund;
import com.investment.advisor.entity.Strategy;
import com.investment.advisor.repository.FundRepository;
import com.investment.advisor.repository.PortfolioFundRepository;
import com.investment.advisor.repository.PortfolioRepository;
import com.investment.advisor.repository.StrategyRepository;
import com.investment.advisor.service.PortfolioService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class PortfolioServiceImpl implements PortfolioService {

    private final PortfolioRepository portfolioRepository;
    private final PortfolioFundRepository portfolioFundRepository;
    private final FundRepository fundRepository;
    private final StrategyRepository strategyRepository;

    @Autowired
    public PortfolioServiceImpl(
            PortfolioRepository portfolioRepository,
            PortfolioFundRepository portfolioFundRepository,
            FundRepository fundRepository,
            StrategyRepository strategyRepository) {
        this.portfolioRepository = portfolioRepository;
        this.portfolioFundRepository = portfolioFundRepository;
        this.fundRepository = fundRepository;
        this.strategyRepository = strategyRepository;
    }

    @Override
    public List<PortfolioDTO> getAllPortfolios() {
        List<Portfolio> portfolios = portfolioRepository.findAll();
        return portfolios.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public PortfolioDTO getPortfolioById(Long id) {
        Portfolio portfolio = portfolioRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("Portfolio not found with id: " + id));
        return convertToDTO(portfolio);
    }

    @Override
    public List<PortfolioDTO> searchPortfolios(String keyword) {
        return portfolioRepository.findByNameContaining(keyword).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<PortfolioDTO> getPortfoliosByStrategyId(Long strategyId) {
        return portfolioRepository.findByStrategyId(strategyId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<PortfolioDTO> getPortfoliosByFundId(Long fundId) {
        return portfolioRepository.findByFundId(fundId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    public List<PortfolioDTO> getPortfoliosByCreator(String creator) {
        return portfolioRepository.findByCreator(creator).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public PortfolioDTO createPortfolio(PortfolioDTO portfolioDTO) {
        Portfolio portfolio = convertToEntity(portfolioDTO);
        
        // 保存组合基本信息
        Portfolio savedPortfolio = portfolioRepository.save(portfolio);
        
        // 如果有基金配置，保存组合基金关联
        if (portfolioDTO.getFunds() != null && portfolioDTO.getWeights() != null) {
            for (FundDTO fundDTO : portfolioDTO.getFunds()) {
                Long fundId = fundDTO.getId();
                BigDecimal weight = portfolioDTO.getWeights().get(fundId);
                
                if (weight != null) {
                    Fund fund = fundRepository.findById(fundId)
                            .orElseThrow(() -> new EntityNotFoundException("Fund not found with id: " + fundId));
                    
                    PortfolioFund portfolioFund = new PortfolioFund();
                    portfolioFund.setPortfolio(savedPortfolio);
                    portfolioFund.setFund(fund);
                    portfolioFund.setWeight(weight);
                    
                    portfolioFundRepository.save(portfolioFund);
                }
            }
        }
        
        return convertToDTO(savedPortfolio);
    }

    @Override
    @Transactional
    public PortfolioDTO updatePortfolio(Long id, PortfolioDTO portfolioDTO) {
        if (!portfolioRepository.existsById(id)) {
            throw new EntityNotFoundException("Portfolio not found with id: " + id);
        }
        
        Portfolio portfolio = convertToEntity(portfolioDTO);
        portfolio.setId(id);
        
        Portfolio updatedPortfolio = portfolioRepository.save(portfolio);
        
        // 更新组合基金关联信息
        if (portfolioDTO.getFunds() != null && portfolioDTO.getWeights() != null) {
            // 先删除旧的关联
            portfolioFundRepository.deleteByPortfolioId(id);
            
            // 添加新的关联
            for (FundDTO fundDTO : portfolioDTO.getFunds()) {
                Long fundId = fundDTO.getId();
                BigDecimal weight = portfolioDTO.getWeights().get(fundId);
                
                if (weight != null) {
                    Fund fund = fundRepository.findById(fundId)
                            .orElseThrow(() -> new EntityNotFoundException("Fund not found with id: " + fundId));
                    
                    PortfolioFund portfolioFund = new PortfolioFund();
                    portfolioFund.setPortfolio(updatedPortfolio);
                    portfolioFund.setFund(fund);
                    portfolioFund.setWeight(weight);
                    
                    portfolioFundRepository.save(portfolioFund);
                }
            }
        }
        
        return convertToDTO(updatedPortfolio);
    }

    @Override
    @Transactional
    public void deletePortfolio(Long id) {
        if (!portfolioRepository.existsById(id)) {
            throw new EntityNotFoundException("Portfolio not found with id: " + id);
        }
        
        // 先删除组合基金关联
        portfolioFundRepository.deleteByPortfolioId(id);
        
        // 再删除组合
        portfolioRepository.deleteById(id);
    }

    @Override
    @Transactional
    public PortfolioDTO updatePortfolioFunds(Long portfolioId, Map<Long, BigDecimal> fundWeights) {
        Portfolio portfolio = portfolioRepository.findById(portfolioId)
                .orElseThrow(() -> new EntityNotFoundException("Portfolio not found with id: " + portfolioId));
        
        // 删除旧的组合基金关联
        portfolioFundRepository.deleteByPortfolioId(portfolioId);
        
        // 添加新的组合基金关联
        for (Map.Entry<Long, BigDecimal> entry : fundWeights.entrySet()) {
            Long fundId = entry.getKey();
            BigDecimal weight = entry.getValue();
            
            Fund fund = fundRepository.findById(fundId)
                    .orElseThrow(() -> new EntityNotFoundException("Fund not found with id: " + fundId));
            
            PortfolioFund portfolioFund = new PortfolioFund();
            portfolioFund.setPortfolio(portfolio);
            portfolioFund.setFund(fund);
            portfolioFund.setWeight(weight);
            
            portfolioFundRepository.save(portfolioFund);
        }
        
        return convertToDTO(portfolio);
    }

    @Override
    @Transactional
    public PortfolioDTO addFundToPortfolio(Long portfolioId, Long fundId, BigDecimal weight) {
        Portfolio portfolio = portfolioRepository.findById(portfolioId)
                .orElseThrow(() -> new EntityNotFoundException("Portfolio not found with id: " + portfolioId));
                
        Fund fund = fundRepository.findById(fundId)
                .orElseThrow(() -> new EntityNotFoundException("Fund not found with id: " + fundId));
        
        // 检查是否已存在该基金
        PortfolioFund existingPortfolioFund = portfolioFundRepository.findByPortfolioAndFund(portfolio, fund);
        
        if (existingPortfolioFund == null) {
            // 添加新的关联
            PortfolioFund portfolioFund = new PortfolioFund();
            portfolioFund.setPortfolio(portfolio);
            portfolioFund.setFund(fund);
            portfolioFund.setWeight(weight);
            
            portfolioFundRepository.save(portfolioFund);
        } else {
            // 更新权重
            existingPortfolioFund.setWeight(weight);
            portfolioFundRepository.save(existingPortfolioFund);
        }
        
        return convertToDTO(portfolio);
    }

    @Override
    @Transactional
    public PortfolioDTO removeFundFromPortfolio(Long portfolioId, Long fundId) {
        if (!portfolioRepository.existsById(portfolioId)) {
            throw new EntityNotFoundException("Portfolio not found with id: " + portfolioId);
        }
        
        if (!fundRepository.existsById(fundId)) {
            throw new EntityNotFoundException("Fund not found with id: " + fundId);
        }
        
        portfolioFundRepository.deleteByPortfolioIdAndFundId(portfolioId, fundId);
        
        return getPortfolioById(portfolioId);
    }

    @Override
    @Transactional
    public PortfolioDTO updateFundWeight(Long portfolioId, Long fundId, BigDecimal weight) {
        Portfolio portfolio = portfolioRepository.findById(portfolioId)
                .orElseThrow(() -> new EntityNotFoundException("Portfolio not found with id: " + portfolioId));
                
        Fund fund = fundRepository.findById(fundId)
                .orElseThrow(() -> new EntityNotFoundException("Fund not found with id: " + fundId));
        
        PortfolioFund portfolioFund = portfolioFundRepository.findByPortfolioAndFund(portfolio, fund);
        
        if (portfolioFund == null) {
            throw new EntityNotFoundException("Fund is not in this portfolio");
        }
        
        portfolioFund.setWeight(weight);
        portfolioFundRepository.save(portfolioFund);
        
        return convertToDTO(portfolio);
    }

    private PortfolioDTO convertToDTO(Portfolio portfolio) {
        PortfolioDTO portfolioDTO = new PortfolioDTO();
        BeanUtils.copyProperties(portfolio, portfolioDTO, "strategy", "funds", "weights");
        
        // 设置策略信息（只复制基本属性，避免循环引用）
        if (portfolio.getStrategy() != null) {
            StrategyDTO strategyDTO = new StrategyDTO();
            Strategy strategy = portfolio.getStrategy();
            // 只复制基本属性，排除可能导致循环引用的字段
            strategyDTO.setId(strategy.getId());
            strategyDTO.setName(strategy.getName());
            strategyDTO.setDescription(strategy.getDescription());
            strategyDTO.setCombinationMethod(strategy.getCombinationMethod());
            strategyDTO.setOptimizeTarget(strategy.getOptimizeTarget());
            strategyDTO.setFundPool(strategy.getFundPool());
            strategyDTO.setCreator(strategy.getCreator());
            strategyDTO.setCreateTime(strategy.getCreateTime());
            strategyDTO.setUpdateTime(strategy.getUpdateTime());
            // 不设置 factors 字段，避免循环引用
            portfolioDTO.setStrategy(strategyDTO);
        }
        
        // 获取组合的基金及权重信息
        List<PortfolioFund> portfolioFunds = portfolioFundRepository.findByPortfolio(portfolio);
        
        if (!portfolioFunds.isEmpty()) {
            List<FundDTO> fundDTOs = new ArrayList<>();
            Map<Long, BigDecimal> weights = new HashMap<>();
            
            for (PortfolioFund portfolioFund : portfolioFunds) {
                Fund fund = portfolioFund.getFund();
                
                FundDTO fundDTO = new FundDTO();
                // 只复制基本属性，避免循环引用
                fundDTO.setId(fund.getId());
                fundDTO.setCode(fund.getCode());
                fundDTO.setName(fund.getName());
                fundDTO.setType(fund.getType());
                fundDTO.setNav(fund.getNav());
                fundDTO.setDailyReturn(fund.getDailyReturn());
                fundDTO.setWeeklyReturn(fund.getWeeklyReturn());
                fundDTO.setMonthlyReturn(fund.getMonthlyReturn());
                fundDTO.setYearlyReturn(fund.getYearlyReturn());
                fundDTO.setSize(fund.getSize());
                fundDTO.setManager(fund.getManager());
                fundDTO.setCompany(fund.getCompany());
                fundDTO.setDescription(fund.getDescription());
                fundDTO.setRating(fund.getRating());
                fundDTO.setCreateTime(fund.getCreateTime());
                fundDTO.setUpdateTime(fund.getUpdateTime());
                // 不设置 tags 字段，避免循环引用
                fundDTOs.add(fundDTO);
                
                weights.put(fund.getId(), portfolioFund.getWeight());
            }
            
            portfolioDTO.setFunds(fundDTOs);
            portfolioDTO.setWeights(weights);
        }
        
        return portfolioDTO;
    }

    private Portfolio convertToEntity(PortfolioDTO portfolioDTO) {
        Portfolio portfolio = new Portfolio();
        BeanUtils.copyProperties(portfolioDTO, portfolio, "id", "strategy", "funds", "weights", "createTime", "updateTime");
        
        // 设置策略
        if (portfolioDTO.getStrategy() != null && portfolioDTO.getStrategy().getId() != null) {
            strategyRepository.findById(portfolioDTO.getStrategy().getId())
                    .ifPresent(portfolio::setStrategy);
        }
        
        return portfolio;
    }
} 